package com.bitcola.security.activity.biz;


import com.alicp.jetcache.anno.CacheType;
import com.alicp.jetcache.anno.Cached;
import com.bitcola.security.activity.entity.UserInfo;
import com.bitcola.security.activity.entity.UserMining;
import com.bitcola.security.activity.entity.UserNet;
import com.bitcola.security.activity.mapper.ColaUserBalanceMapper;
import com.bitcola.security.activity.mapper.ColaUserMapper;
import com.bitcola.security.activity.mapper.UserMiningMapper;
import com.bitcola.security.activity.vo.MiningGroupInfo;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Service
@Log4j2
public class UserInviterBiz implements ApplicationRunner, Ordered {
    @Autowired
    ColaUserBalanceMapper userBalanceMapper;

    public volatile static Map<String, UserNet> userNetMap = new ConcurrentHashMap<>(2000);


    @Autowired
    RedisTemplate<String, Object> redisTemplate;

    @Resource
    ColaUserMapper userMapper;
    @Autowired
    UserMiningMapper userMiningMapper;

    public int init() {
        Map<String, UserNet> userNetMapTemp = new ConcurrentHashMap<>(2000);

        var userInfos = userBalanceMapper.getUserInfo();
        for (UserInfo userInfo : userInfos) {
            UserNet child = userNetMapTemp.getOrDefault(userInfo.getChild(), UserNet.builder().userId(userInfo.getChild()).build());
            child.setKyc(userInfo.getKyc());
            if (child.getChild() == null) child.init();
            child.setEmail(userInfo.getEmail());
            child.setTel(userInfo.getPhone());
            if (userInfo.getInviter() != null) {
                child.setInviter(userInfo.getInviter());
                UserNet inviter = userNetMapTemp.getOrDefault(userInfo.getInviter(), UserNet.builder().userId(userInfo.getInviter()).build());
                if (inviter.getChild() == null) inviter.init();
                inviter.getChild().add(child);
                userNetMapTemp.put(userInfo.getInviter(), inviter);
            }
            userNetMapTemp.put(child.getUserId(), child);
        }
        userNetMap = userNetMapTemp;

        for (UserNet net : userNetMap.values()) {
            addUser(net);

        }

        var userMinings = userMiningMapper.selectAll();
        for (UserMining mining : userMinings) {
            addPower(mining);
        }
        log.info("初始化完成关联关系,共同加载:{}", userNetMap.size());
        var net = userNetMap.get("10004");
        return userNetMap.size();
    }

    public void addUser(UserNet net) {
        var netTemp = net;
        if (net.getInviter() != null) {
            var inviter = userNetMap.get(netTemp.getInviter());
            if (inviter == null) return;
            inviter.addDirectNum(1);
            while (netTemp.getInviter() != null) {
                inviter = userNetMap.get(netTemp.getInviter());
                inviter.addGroupNum(1);
                netTemp = inviter;

            }
        }


    }

    public void addKyc(String userId) {
        var net = userNetMap.get(userId);
        net.setKyc("LV1_PASS");
        var netTemp = net;
        int tag = isKycPass(netTemp.getKyc()) ? 1 : 0;


        if (net.getInviter() != null) {
            var inviter = userNetMap.get(netTemp.getInviter());
            inviter.addDirectKyc(1);
            while (netTemp.getInviter() != null) {
                inviter = userNetMap.get(netTemp.getInviter());
                inviter.addGroupKyc(1);
                netTemp = inviter;
            }
        }

    }


    public void addPower(UserMining userMining) {
        String userId = userMining.getUserId();
        var net = userNetMap.get(userId);
        if (net == null) return;
        int tag = (int) net.getUserMinings().stream().filter(x -> x.getLevel() > 0).count();
        tag = userMining.getPower().compareTo(BigDecimal.ZERO) > 0 && tag == 0 ? 1 : 0;


//        String key="user_mining_power_"+userMining.getUserId();
        if (userMining.isActive()) net.addSelfPower(userMining.getPower());
//        if(redisTemplate.opsForValue().get(key)==null) redisTemplate.opsForValue().set(key,0d);
//        redisTemplate.opsForValue().increment("user_mining_power_"+userMining.getUserId(),userMining.getPower().doubleValue());
        net.addGained(userMining.getGained());
//        net.addGroupMiningNum(tag);
//        net.addGroupPower(userMining.getPower());
        net.getUserMinings().add(userMining);
        var netTemp = net;

        if (net.getInviter() != null) {
            var inviter = userNetMap.get(net.getInviter());
            if (inviter != null) {

                inviter.addDirectMiningNum(tag);
                if (userMining.isActive()) inviter.addDirectPower(userMining.getPower());
                while (netTemp.getInviter() != null) {
                    inviter = userNetMap.get(netTemp.getInviter());
                    if (inviter != null) {

                        if (netTemp != net) {
                            netTemp.addGroupMiningNum(tag);
                            if (userMining.isActive()) netTemp.addGroupPower(userMining.getPower());
                        }
                        netTemp = inviter;

                    }
                }
            }
        }


    }

    public static boolean isKycPass(String kycStatus) {
        return ("LV1_PASS".equals(kycStatus));
    }

    public BigDecimal getMiningPower(UserNet userNet) {

        long now = System.currentTimeMillis();
        BigDecimal power = BigDecimal.ZERO;
        if (userNet.getUserMinings() != null) {
            for (UserMining userMining : userNet.getUserMinings()) {
                if (userMining.isActive()) {
                    power = power.add(userMining.getPower());
                }

            }

        }
        return power;

    }

    public List<UserNet> getLevel1(String inviter) {

        var net = userNetMap.get(inviter);
        if (net != null) {
            var result = net.getChild();
            return result;
        }
        return new ArrayList<>(0);
    }

    public List<UserNet> getLevel2(String inviter) {
        var net = userNetMap.get(inviter);
        if (net != null) {
            var childs = net.getChild();
            List<UserNet> results = new ArrayList<>();

            for (UserNet userNet : childs) {
                results.addAll(getLevel1(userNet.getUserId()));
            }
            return results;
        }
        return new ArrayList<>(0);
    }

    public List<UserNet> getLevel3(String inviter) {
        var net = userNetMap.get(inviter);
        if (net != null) {
            var childs = net.getChild();
            List<UserNet> results = new ArrayList<>();

            for (UserNet userNet : childs) {
                results.addAll(getLevel2(userNet.getUserId()));
            }
            return results;
        }
        return new ArrayList<>(0);
    }

    @Cached(cacheType = CacheType.LOCAL, expire = 5)
    public MiningGroupInfo getGroupInfo(String userId, int level) {
        var userNet = userNetMap.get(userId);
        List<UserNet> queue = new ArrayList<>();
        queue.add(userNet);
        BigDecimal result = BigDecimal.ZERO;
        long now = System.currentTimeMillis();

        long count = 1;
        for (int i = 0; i < level; i++) {
            ArrayList<UserNet> levelsQueue = new ArrayList<>();
            int len = 0;
            for (UserNet net : queue) {
                if (net.getChild().size() > 0) {
                    levelsQueue.addAll(net.getChild());
                    ++count;
                    result = result.add(getPower(net));

                } else ++len;
            }
            if (len == queue.size()) break;
            queue = levelsQueue;
        }
        result = getPower(userNet);

        return MiningGroupInfo.builder().power(result).count(count).build();


    }

    private BigDecimal getPower(UserNet userNet) {
        BigDecimal result = BigDecimal.ZERO;
        long now = System.currentTimeMillis();
        var minings = userNet.getUserMinings();
        for (UserMining mining : minings) {
            if (mining.isActive())
                result = result.add(mining.getTransform());
        }
        return result;
    }


    public int getDirect(String userId) {

        var net = userNetMap.get(userId);
        if (net != null) return net.getChild().size();
        return 0;
    }

    public BigDecimal getSelfPower(String userId) {
        var net = userNetMap.get(userId);
        if (net != null) {
            return getPower(net);


        }
        return BigDecimal.ZERO;

    }

    public synchronized void createNewInvitor(String userId, String invitorCode) {
        UserNet userNet = null;
        var invitor = userMapper.infoByInviterCode(invitorCode);
        var user = userMapper.info(userId);
        if (userNetMap.containsKey(userId)) {
            userNet=userNetMap.get(userId);
        }else{
            userNet = UserNet.builder().userId(userId).email(user.getEmail()).tel(user.getTelPhone()).build();
            userNet.init();

        }
        if (StringUtils.isNotEmpty(invitorCode) && invitor != null) {
            var net = userNetMap.get(invitor.getId());
            if (net != null && !net.getUserId().equals(userId)) {

                userNet.setInviter(net.getUserId());
                net.getChild().add(userNet);


            }
        }
        userNetMap.put(userId, userNet);
        addUser(userNet);
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {


        init();

    }

    @Override
    public int getOrder() {
        return 10;
    }
}
